import discord
import time
import asyncio

from .exceptions import ExtractionError, WrongEntryTypeError, LiveStreamError
from .entry import get_header


class YoutubeDLSource(discord.FFmpegPCMAudio):
    def __init__(self, playlist, url):
        self._process = None
        self.playlist = playlist
        self.loop = playlist.loop
        self.downloader = playlist.downloader
        self.url = url
        self.info = None
        self.ready = False
        self.error = False

    async def get_info(self):
        try:
            # First attempt to gather the information
            info = await self.downloader.extract_info(self.loop, self.url, download=False)
        except Exception as e:
            raise ExtractionError('Could not extract information from {}\n\n{}'.format(self.url, e))

        # Check if a playlist was provided
        if info.get('_type', None) == 'playlist':
            # It is possible that the 'playlist' is the search
            if info.get('extractor') == 'youtube:search':
                # If so, and we have no entries, then nothing with this search was found
                if len(info['entries']) == 0:
                    raise ExtractionError('Could not extract information from %s' % self.url)
                # Otherwise get the first result
                else:
                    info = info['entries'][0]
            # If this isn't a search, then it is a playlist, this can't be done
            else:
                raise WrongEntryTypeError("This is a playlist.", True,
                                          info.get('webpage_url', None) or info.get('url', None))

        if info['extractor'] in ['generic', 'Dropbox']:
            try:
                # unfortunately this is literally broken
                # https://github.com/KeepSafe/aiohttp/issues/758
                # https://github.com/KeepSafe/aiohttp/issues/852
                headers = await get_header(info['url'])
                content_type = headers.get('Content-Type')
            except Exception as e:
                content_type = None

            if content_type:
                if content_type.startswith(('application/', 'image/')):
                    if '/ogg' not in content_type:  # How does a server say `application/ogg` what the actual fuck
                        raise ExtractionError("Invalid content type \"%s\" for url %s" % (content_type, self.url))
                if headers.get('ice-audio-info'):
                    raise LiveStreamError("Cannot download from a livestream")

        if info.get('is_live', False):
            raise LiveStreamError("Cannot download from a livestream")

        # Set our info
        self.info = info

    async def prepare(self):
        await self.get_info()
        asyncio.run_coroutine_threadsafe(self.download(), self.loop)
        return self.info

    async def download(self):
        try:
            result = await self.downloader.extract_info(self.loop, self.url, download=True)
        except Exception as e:
            self.error = True
            raise ExtractionError(e)
        if result:
            self.ready = True
            opts = {
                'before_options': '-nostdin',
                'options': '-vn -b:a 128k -v fatal'
            }
            super().__init__(self.downloader.ytdl.prepare_filename(self.info), **opts)

    @property
    def title(self):
        return self.info.get('title', 'Untitled')

    @property
    def thumbnail(self):
        return self.info.get('thumbnail', None)

    @property
    def length(self):
        return self.info.get('duration') or 0

    @property
    def progress(self):
        if hasattr(self, 'start_time') and self.start_time:
            return round(time.time() - self.start_time)

    @property
    def remaining(self):
        length = self.length
        progress = self.progress
        if length and progress:
            return length - progress

    @property
    def embed(self):
        """A property that returns an embed that can be used to display information about this particular song"""
        # Create the embed object we'll use
        embed = discord.Embed()
        # Fill in the simple things
        embed.add_field(name='Title', value=self.title, inline=False)
        embed.add_field(name='Requester', value=self.requester.display_name, inline=False)
        embed.add_field(name='Place in Queue', value=str(self.playlist.count))
        if self.thumbnail:
            embed.set_thumbnail(url=self.thumbnail)
        # Get the current length of the song and display this
        if self.length:
            length = divmod(round(self.length, 0), 60)
            fmt = "{0[0]}m {0[1]}s".format(length)
            embed.add_field(name='Duration', value=fmt, inline=False)
        # And return the embed we created
        return embed

class YoutubeDLLiveStreamSource(discord.FFmpegPCMAudio):
    def __init__(self, bot, url):
        self._process = None
        self.downloader = bot.downloader
        self.loop = bot.loop
        self.url = url

    @property
    def title(self):
        return self.info.get('title', 'Untitled')

    @property
    def thumbnail(self):
        return self.info.get('thumbnail', None)

    @property
    def embed(self):
        """A property that returns an embed that can be used to display information about this particular song"""
        # Create the embed object we'll use
        embed = discord.Embed()
        # Fill in the simple things
        embed.add_field(name='Title', value=self.title, inline=False)
        embed.add_field(name='Requester', value=self.requester.display_name, inline=False)
        if self.thumbnail:
            embed.set_thumbnail(url=self.thumbnail)
        # And return the embed we created
        return embed

    async def get_ready(self):
        try:
            # First attempt to gather the information
            info = await self.downloader.extract_info(self.loop, self.url, download=False)
        except Exception as e:
            raise ExtractionError('Could not extract information from {}\n\n{}'.format(self.url, e))

        if not info.get('is_live', False):
            raise WrongEntryTypeError("This is not a livestream!", True, info.get('webpage_url', None) or info.get('url', None))

        # Set our info
        self.info = info
        opts = {
            'before_options': '-nostdin',
            'options': '-vn -b:a 128k -v fatal'
        }
        super().__init__(info.get('manifest_url'), **opts)
