from typing import TYPE_CHECKING, Union, Tuple, Dict, Optional

if TYPE_CHECKING:
    from . import SessionObject
    from os import PathLike


class Meme():
    """| Base class for :class:`~imgflip.SyncMeme` and :class:`~imgflip.AsyncMeme`
    | ``str(Meme_object)`` would return the image url of the meme.

    Attributes
    ----------
    template_id: :class:`int`
        the id of the meme template
    url: :class:`str`
        the image url of the meme
    page_url: :class:`str`
        the url of the imgflip page of the meme
    """
    def __init__(
        self,
        template_id: int,
        url: str,
        page_url: str,
        session: "SessionObject"
    ):
        self.template_id: int = int(template_id)
        self.url: str = url
        self.page_url: str = page_url
        self.session: SessionObject = session

    def __str__(self) -> str:
        """gets the url of the meme"""
        return self.url


class SyncMeme(Meme):
    """This is a subclass of :class:`~imgflip.Meme`. 
    It is returned in :meth:`~imgflip.Imgflip.make_meme` 
    when the session passed is ``requests.Session``
    """
    def read(self) -> bytes:
        """Reads the meme and get the bytes of the image

        Returns
        -------
        :class:`bytes`
            the image of the meme in bytes
        """
        resp = self.session.get(self.url)
        return resp.content

    def save(self, fp: "PathLike") -> None:
        """Saves the meme image in a file. This returns nothing.

        Parameters
        ----------
        fp: :class:`os.PathLike`
            the file path to save the image to.
        """
        img = self.read()
        with open(fp, "wb") as f:
            f.write(img)


class AsyncMeme(Meme):
    """This is a subclass of :class:`~imgflip.Meme`. 
    It is returned in :meth:`~imgflip.Imgflip.make_meme` 
    when the session passed is ``aiohttp.ClientSession``
    """
    async def read(self) -> bytes:
        """| This function is a |coro|_
        | Reads the meme and get the bytes of the image

        Returns
        -------
        :class:`bytes`
            the image of the meme in bytes
        """
        async with self.session.get(self.url) as resp:
            img = await resp.read()
        return img

    async def save(self, fp: "PathLike") -> None:
        """| This function is a |coro|_
        | Saves the meme image in a file.

        Parameters
        ----------
        fp: :class:`os.PathLike`
            the file path to save the image to.
        """
        img = await self.read()
        with open(fp, "wb") as f:
            f.write(img)


class Box():
    """| Represents a text box that can be used in the ``boxes``
      parameter of :meth:`~imgflip.Imgflip.make_meme`. 
    | ``str(box_obj)`` will return the text of the box.

    Parameters
    ----------
    text: :class:`str`
        the text on the box
    position: Tuple[:class:`int`, :class:`int`]
        the position of the box on the meme in the format (x, y)
    size: Tuple[:class:`int`, :class:`int`]
        the size of the box in the format (width, height)
    color: Optional[:class:`str`]
        the hex color of the text on the box. Defaults to ``"#ffffff"``
    outline_color: Optional[:class:`str`]
        the outline hex color of the text on the box. Defaults to ``"#000000"``
    """
    def __init__(
        self,
        text: str,
        position: Tuple[int, int],
        size: Tuple[int, int],
        color: Optional[str] = "#ffffff",
        outline_color: Optional[str] = "#000000"
    ):
        self.text: str = text
        self.position: Tuple[int, int] = position
        self.x: int = position[0]
        self.y: int = position[1]
        self.size: Tuple[int, int] = size
        self.width: int = size[0]
        self.height: int = size[1]
        self.color: str = color
        self.outline_color: str = outline_color

        self._raw: Dict[str, Union[str, int]] = {
            "text": text,
            "x": position[0],
            "y": position[1],
            "width": size[0],
            "height": size[1],
            "color": color,
            "outline_color": outline_color
        }

    def __str__(self) -> str:
        """gets the text of the box"""
        return self.text

class Template():
    """| Represents a meme template which can be passed into 
      :meth:`~imgflip.Imgflip.make_meme`.
    | ``int(template_obj)`` will return the id of the template.
    | ``str(template_obj)`` will return the name of the template.
    
    Attributes
    ----------
    id: :class:`int`
        the template id
    name: Optional[:class:`str`]
        the name of the template
    url: Optional[:class:`str`]
        the image url of the template
    width: Optional[:class:`int`]
        the width of the template image
    height: Optional[:class:`int`]
        the height of the template image
    box_count: Optional[:class:`int`]
        the number of boxes in the template
    """
    def __init__(
        self,
        id: int,
        name: Optional[str] = None,
        url: Optional[str] = None,
        width: Optional[int] = None,
        height: Optional[int] = None,
        box_count: Optional[int] = None
    ):
        self.id: int = int(id)
        self.name: Optional[str] = name
        self.url: Optional[str] = url
        self.width: Optional[int] = int(width)
        self.height: Optional[int] = int(height)
        self.box_count: Optional[int] = int(box_count)

    def __str__(self) -> str:
        """get the template name"""
        return str(self.name)

    def __int__(self) -> int:
        """get the template id"""
        return self.id