# Path: core/pageable.py
# class MessageContent(namedtuple('MessageContent', ('text', 'embed'))):
#     __slots__ = ()
# 
# class Pageable:
#     """
#     An interface that provide a "paging" functionality to `discord.Message`.
# 
#     === Attributes ===
#     :type message: Message
#         The original `discord.Message` object.
#     :type author: Union[Member, User]
#         The original `discord.Message` requester.
#     :type timeout: int
#         Amount of seconds of inactivity before the message self destructs.
#     """
#     __slots__ = ('message', 'author', 'timeout', 'emojis')
# 
#     def __init__(self, message: Message, author: Union[Member, User],
#                  timeout: int):
#         """
#         :param message: The original `discord.Message` object.
# 
#         :param author: The original `discord.Message` requester.
# 
#         :param timeout:
#             Amount of seconds of inactivity before the message self destructs.
#         """
#         self.message = message
#         self.author = author
#         self.timeout = timeout
#         self.emojis = ('⏪', '⏩', '❌')
# 
#     async def prev(self, ctx) -> Optional[MessageContent]:
#         """
#         Return the previous page.
#         This should return None if there are no previous page.
# 
#         :param ctx: `discord.Context` object.
# 
#         :return: a `MessageContent` instance that represents the previous page.
#         """
#         raise NotImplementedError
# 
#     async def next(self, ctx) -> Optional[MessageContent]:
#         """
#         Return the next page.
#         This should return None if there are no next page.
# 
#         :param ctx: `discord.Context` object.
# 
#         :return: a `MessageContent` instance that represents the next page.
#         """
#         raise NotImplementedError
# 
#     async def __do_action(self, ctx, emoji):
#         """
#         Edit/delete `self.message`.
# 
#         :param ctx: `discord.Context` object.
# 
#         :param emoji: the user reaction emoji.
#         """
#         edit = None
#         if emoji == self.emojis[0]:
#             edit = await self.prev(ctx)
#         if emoji == self.emojis[1]:
#             edit = await self.next(ctx)
#         if emoji == self.emojis[2]:
#             await self.message.delete()
#             self.message = None
#         if edit and self.message:
#             await self.message.edit(content=edit.text, embed=edit.embed)
# 
#     async def __call__(self, ctx: Context):
#         """
#         The main loop that check for user reactions and flip pages.
# 
#         :param ctx: `discord.Context` object.
#         """
#         for emoji in self.emojis:
#             await self.message.add_reaction(emoji)
#         while True:
#             if not self.message:
#                 return
#             try:
#                 reaction, user = await ctx.bot.wait_for(
#                     'reaction_add',
#                     timeout=self.timeout
#                 )
#                 emoji = reaction.emoji
#                 if emoji not in self.emojis \
#                         or user not in (self.author, ctx.bot.user):
#                     await self.message.remove_reaction(emoji, user)
#                 if user == self.author:
#                     await self.__do_action(ctx, emoji)
#             except TimeoutError:
#                 await self.message.delete()
#                 return
# 
# Path: music/music_util.py
# def add_embed_options(embed: Embed) -> Embed:
#     embed.add_field(name='Previous Page', value=':rewind:')
#     embed.add_field(name='Next Page', value=':fast_forward:')
#     embed.add_field(name='Exit', value=':x:')
#     embed.set_footer(text='This message will self destruct after'
#                           '1 minute of inactivity.')
#     return embed
# 
# def playlist_embed(ctx: Context, cur_page: int, total_page: int,
#                    current, section: list) -> Optional[Embed]:
#     """
#     Get an embed representation of a section of a audio playlist.
# 
#     :param ctx: the `discord.Context` object.
# 
#     :param cur_page: the current page number.
# 
#     :param total_page: the total page count.
# 
#     :param current: the currently playing `Entry`
#     :type current: Entry
# 
#     :param section: a list of `Entry` in the section.
#     :type section: List[Entry]
# 
#     :return: An embed representation of a section of a audio playlist, if any.
#     """
#     if not section and not current:
#         return
#     colour = ctx.bot.config.colour
#     embed = Embed(
#         colour=colour,
#         title=f'Playlist page {cur_page} of {total_page}'
#     )
#     embed.add_field(
#         name='Currently Playing',
#         value=str(current),
#         inline=False
#     )
#     if section:
#         section_str = '\n'.join(
#             f'{i+1+((cur_page -1)* 10)}. {entry}'
#             for i, entry in enumerate(section)
#         )
#         embed.add_field(name='Up Next', value=section_str, inline=False)
#     return embed


from typing import Optional, Tuple, Union
from discord import Embed, Member, Message, User
from discord.ext.commands import Context
from core import MessageContent, Pageable
from music.music_util import add_embed_options, playlist_embed
        :type player: AbstractMusicPlayer
        """
        self.player = player
        self.cur_page = 1
        super().__init__(message, author, 60)

    def __get_page_index(self, diff: int) -> Tuple[int, int]:
        """
        Get the start and end page index for `self.cur_page` + `diff`
        :param diff: the difference to change the page by.
        :return: the start and end page index for `self.cur_page` + `diff`
        """
        end = 10 * (self.cur_page + diff)
        return end - 10, end

    def __embed(self, section: list, ctx: Context) -> Optional[Embed]:
        """
        Get an embed for the give section of `Entry`.

        :param section: a list of `Entry` to put in the embed.

        :param ctx: the `discord.Context` object.

        :return: an embed for the give section of `Entry`, if any.
        """
        embed = playlist_embed(
            ctx, self.cur_page, self.player.total_page,
            self.player.current, section
        )
        if embed:

# Path: core/pageable.py
# class MessageContent(namedtuple('MessageContent', ('text', 'embed'))):
#     __slots__ = ()
# 
# class Pageable:
#     """
#     An interface that provide a "paging" functionality to `discord.Message`.
# 
#     === Attributes ===
#     :type message: Message
#         The original `discord.Message` object.
#     :type author: Union[Member, User]
#         The original `discord.Message` requester.
#     :type timeout: int
#         Amount of seconds of inactivity before the message self destructs.
#     """
#     __slots__ = ('message', 'author', 'timeout', 'emojis')
# 
#     def __init__(self, message: Message, author: Union[Member, User],
#                  timeout: int):
#         """
#         :param message: The original `discord.Message` object.
# 
#         :param author: The original `discord.Message` requester.
# 
#         :param timeout:
#             Amount of seconds of inactivity before the message self destructs.
#         """
#         self.message = message
#         self.author = author
#         self.timeout = timeout
#         self.emojis = ('⏪', '⏩', '❌')
# 
#     async def prev(self, ctx) -> Optional[MessageContent]:
#         """
#         Return the previous page.
#         This should return None if there are no previous page.
# 
#         :param ctx: `discord.Context` object.
# 
#         :return: a `MessageContent` instance that represents the previous page.
#         """
#         raise NotImplementedError
# 
#     async def next(self, ctx) -> Optional[MessageContent]:
#         """
#         Return the next page.
#         This should return None if there are no next page.
# 
#         :param ctx: `discord.Context` object.
# 
#         :return: a `MessageContent` instance that represents the next page.
#         """
#         raise NotImplementedError
# 
#     async def __do_action(self, ctx, emoji):
#         """
#         Edit/delete `self.message`.
# 
#         :param ctx: `discord.Context` object.
# 
#         :param emoji: the user reaction emoji.
#         """
#         edit = None
#         if emoji == self.emojis[0]:
#             edit = await self.prev(ctx)
#         if emoji == self.emojis[1]:
#             edit = await self.next(ctx)
#         if emoji == self.emojis[2]:
#             await self.message.delete()
#             self.message = None
#         if edit and self.message:
#             await self.message.edit(content=edit.text, embed=edit.embed)
# 
#     async def __call__(self, ctx: Context):
#         """
#         The main loop that check for user reactions and flip pages.
# 
#         :param ctx: `discord.Context` object.
#         """
#         for emoji in self.emojis:
#             await self.message.add_reaction(emoji)
#         while True:
#             if not self.message:
#                 return
#             try:
#                 reaction, user = await ctx.bot.wait_for(
#                     'reaction_add',
#                     timeout=self.timeout
#                 )
#                 emoji = reaction.emoji
#                 if emoji not in self.emojis \
#                         or user not in (self.author, ctx.bot.user):
#                     await self.message.remove_reaction(emoji, user)
#                 if user == self.author:
#                     await self.__do_action(ctx, emoji)
#             except TimeoutError:
#                 await self.message.delete()
#                 return
# 
# Path: music/music_util.py
# def add_embed_options(embed: Embed) -> Embed:
#     embed.add_field(name='Previous Page', value=':rewind:')
#     embed.add_field(name='Next Page', value=':fast_forward:')
#     embed.add_field(name='Exit', value=':x:')
#     embed.set_footer(text='This message will self destruct after'
#                           '1 minute of inactivity.')
#     return embed
# 
# def playlist_embed(ctx: Context, cur_page: int, total_page: int,
#                    current, section: list) -> Optional[Embed]:
#     """
#     Get an embed representation of a section of a audio playlist.
# 
#     :param ctx: the `discord.Context` object.
# 
#     :param cur_page: the current page number.
# 
#     :param total_page: the total page count.
# 
#     :param current: the currently playing `Entry`
#     :type current: Entry
# 
#     :param section: a list of `Entry` in the section.
#     :type section: List[Entry]
# 
#     :return: An embed representation of a section of a audio playlist, if any.
#     """
#     if not section and not current:
#         return
#     colour = ctx.bot.config.colour
#     embed = Embed(
#         colour=colour,
#         title=f'Playlist page {cur_page} of {total_page}'
#     )
#     embed.add_field(
#         name='Currently Playing',
#         value=str(current),
#         inline=False
#     )
#     if section:
#         section_str = '\n'.join(
#             f'{i+1+((cur_page -1)* 10)}. {entry}'
#             for i, entry in enumerate(section)
#         )
#         embed.add_field(name='Up Next', value=section_str, inline=False)
#     return embed


# Path: music/playlist.py
from typing import Optional, Tuple, Union
from discord import Embed, Member, Message, User
from discord.ext.commands import Context
from core import MessageContent, Pageable
from music.music_util import add_embed_options, playlist_embed

        :type player: AbstractMusicPlayer
        """
        self.player = player
        self.cur_page = 1
        super().__init__(message, author, 60)

    def __get_page_index(self, diff: int) -> Tuple[int, int]:
        """
        Get the start and end page index for `self.cur_page` + `diff`
        :param diff: the difference to change the page by.
        :return: the start and end page index for `self.cur_page` + `diff`
        """
        end = 10 * (self.cur_page + diff)
        return end - 10, end

    def __embed(self, section: list, ctx: Context) -> Optional[Embed]:
        """
        Get an embed for the give section of `Entry`.

        :param section: a list of `Entry` to put in the embed.

        :param ctx: the `discord.Context` object.

        :return: an embed for the give section of `Entry`, if any.
        """
        embed = playlist_embed(
            ctx, self.cur_page, self.player.total_page,
            self.player.current, section
        )
        if embed:

            return add_embed_options(embed)