from discord.ext import commands
from .utils import checks
import aiohttp
import json
import discord
from lxml import etree

class CodeBlock:
    missing_error = 'Missing code block. Please use the following markdown\n\\`\\`\\`language\ncode here\n\\`\\`\\`'
    def __init__(self, argument):
        try:
            block, code = argument.split('\n', 1)
        except ValueError:
            raise commands.BadArgument(self.missing_error)

        if not block.startswith('```') and not code.endswith('```'):
            raise commands.BadArgument(self.missing_error)

        language = block[3:]
        self.command = self.get_command_from_language(language.lower())
        self.source = code.rstrip('`')

    def get_command_from_language(self, language):
        cmds = {
            'cpp': 'g++ -std=c++1z -O2 -Wall -Wextra -pedantic -pthread main.cpp -lstdc++fs && ./a.out',
            'c': 'mv main.cpp main.c && gcc -std=c11 -O2 -Wall -Wextra -pedantic main.c && ./a.out',
            'py': 'python main.cpp', # coliru has no python3
            'python': 'python main.cpp',
            'haskell': 'runhaskell main.cpp'
        }

        cpp = cmds['cpp']
        for alias in ('cc', 'h', 'c++', 'h++', 'hpp'):
            cmds[alias] = cpp
        try:
            return cmds[language]
        except KeyError as e:
            if language:
                fmt = 'Unknown language to compile for: {}'.format(language)
            else:
                fmt = 'Could not find a language to compile with.'
            raise commands.BadArgument(fmt) from e

class Lounge:
    """Commands made for Lounge<C++>.

    Don't abuse these.
    """

    def __init__(self, bot):
        self.bot = bot
        self.session = aiohttp.ClientSession(loop=bot.loop)

    def __unload(self):
        self.session.close()

    @commands.command(pass_context=True)
    async def coliru(self, ctx, *, code: CodeBlock):
        """Compiles code via Coliru.

        You have to pass in a code block with the language syntax
        either set to one of these:

        - cpp
        - c
        - python
        - py
        - haskell

        Anything else isn't supported. The C++ compiler uses g++ -std=c++14.

        The python support is only python2.7 (unfortunately).

        Please don't spam this for Stacked's sake.
        """
        payload = {
            'cmd': code.command,
            'src': code.source
        }

        data = json.dumps(payload)

        await self.bot.type()
        async with self.session.post('http://coliru.stacked-crooked.com/compile', data=data) as resp:
            if resp.status != 200:
                await self.bot.say('Coliru did not respond in time.')
                return
            output = await resp.text()

            if len(output) < 1992:
                fmt = '```\n{}\n```'.format(output)
                await self.bot.say(fmt)
                return

            # output is too big so post it in gist
            async with self.session.post('http://coliru.stacked-crooked.com/share', data=data) as resp:
                if resp.status != 200:
                    await self.bot.say('Could not create coliru shared link')
                else:
                    shared_id = await resp.text()
                    await self.bot.say('Output too big. Coliru link: http://coliru.stacked-crooked.com/a/' + shared_id)

    @coliru.error
    async def coliru_error(self, error, ctx):
        if isinstance(error, commands.BadArgument):
            await self.bot.say(error)
        if isinstance(error, commands.MissingRequiredArgument):
            await self.bot.say(CodeBlock.missing_error)

    @commands.command()
    async def cpp(self, *, query: str):
        """Search something on cppreference"""

        url = 'http://en.cppreference.com/w/cpp/index.php'
        params = {
            'title': 'Special:Search',
            'search': query
        }

        async with self.session.get(url, params=params) as resp:
            if resp.status != 200:
                return await self.bot.say('An error occurred (status code: {0.status}). Retry later.'.format(resp))

            if len(resp.history) > 0:
                return await self.bot.say(resp.url)

            e = discord.Embed()
            root = etree.fromstring(await resp.text(), etree.HTMLParser())

            nodes = root.findall(".//div[@class='mw-search-result-heading']/a")

            description = []
            special_pages = []
            fmt = '[`{0}`](http://en.cppreference.com{1})'
            special_fmt = '[{}](http://en.cppreference.com{})'
            for node in nodes:
                href = node.attrib['href']
                if not href.startswith('/w/cpp'):
                    continue

                if href.startswith(('/w/cpp/language', '/w/cpp/concept')):
                    # special page
                    special_pages.append(special_fmt.format(node.text, href))
                else:
                    description.append(fmt.format(node.text, href))

            if len(special_pages) > 0:
                e.add_field(name='Language Results', value='\n'.join(special_pages), inline=False)
                if len(description):
                    e.add_field(name='Library Results', value='\n'.join(description[:10]), inline=False)
            else:
                if not len(description):
                    return await self.bot.say('No results found.')

                e.title = 'Search Results'
                e.description = '\n'.join(description[:15])

            await self.bot.say(embed=e)

def setup(bot):
    bot.add_cog(Lounge(bot))

from discord.ext import commands
from .utils import checks
import aiohttp
import json
import discord
from lxml import etree

class CodeBlock:
    missing_error = 'Missing code block. Please use the following markdown\n\\`\\`\\`language\ncode here\n\\`\\`\\`'
    def __init__(self, argument):
        try:
            block, code = argument.split('\n', 1)
        except ValueError:
            raise commands.BadArgument(self.missing_error)

        if not block.startswith('```') and not code.endswith('```'):
            raise commands.BadArgument(self.missing_error)

        language = block[3:]
        self.command = self.get_command_from_language(language.lower())
        self.source = code.rstrip('`')

    def get_command_from_language(self, language):
        cmds = {
            'cpp': 'g++ -std=c++1z -O2 -Wall -Wextra -pedantic -pthread main.cpp -lstdc++fs && ./a.out',
            'c': 'mv main.cpp main.c && gcc -std=c11 -O2 -Wall -Wextra -pedantic main.c && ./a.out',
            'py': 'python main.cpp', # coliru has no python3
            'python': 'python main.cpp',
            'haskell': 'runhaskell main.cpp'
        }

        cpp = cmds['cpp']
        for alias in ('cc', 'h', 'c++', 'h++', 'hpp'):
            cmds[alias] = cpp
        try:
            return cmds[language]
        except KeyError as e:
            if language:
                fmt = 'Unknown language to compile for: {}'.format(language)
            else:
                fmt = 'Could not find a language to compile with.'
            raise commands.BadArgument(fmt) from e

class Lounge:
    """Commands made for Lounge<C++>.

    Don't abuse these.
    """

    def __init__(self, bot):
        self.bot = bot
        self.session = aiohttp.ClientSession(loop=bot.loop)

    def __unload(self):
        self.session.close()

    @commands.command(pass_context=True)
    async def coliru(self, ctx, *, code: CodeBlock):
        """Compiles code via Coliru.

        You have to pass in a code block with the language syntax
        either set to one of these:

        - cpp
        - c
        - python
        - py
        - haskell

        Anything else isn't supported. The C++ compiler uses g++ -std=c++14.

        The python support is only python2.7 (unfortunately).

        Please don't spam this for Stacked's sake.
        """
        payload = {
            'cmd': code.command,
            'src': code.source
        }

        data = json.dumps(payload)

        await self.bot.type()
        async with self.session.post('http://coliru.stacked-crooked.com/compile', data=data) as resp:
            if resp.status != 200:
                await self.bot.say('Coliru did not respond in time.')
                return
            output = await resp.text()

            if len(output) < 1992:
                fmt = '```\n{}\n```'.format(output)
                await self.bot.say(fmt)
                return

            # output is too big so post it in gist
            async with self.session.post('http://coliru.stacked-crooked.com/share', data=data) as resp:
                if resp.status != 200:
                    await self.bot.say('Could not create coliru shared link')
                else:
                    shared_id = await resp.text()
                    await self.bot.say('Output too big. Coliru link: http://coliru.stacked-crooked.com/a/' + shared_id)

    @coliru.error
    async def coliru_error(self, error, ctx):
        if isinstance(error, commands.BadArgument):
            await self.bot.say(error)
        if isinstance(error, commands.MissingRequiredArgument):
            await self.bot.say(CodeBlock.missing_error)

    @commands.command()
    async def cpp(self, *, query: str):
        """Search something on cppreference"""

        url = 'http://en.cppreference.com/w/cpp/index.php'
        params = {
            'title': 'Special:Search',
            'search': query
        }

        async with self.session.get(url, params=params) as resp:
            if resp.status != 200:
                return await self.bot.say('An error occurred (status code: {0.status}). Retry later.'.format(resp))

            if len(resp.history) > 0:
                return await self.bot.say(resp.url)

            e = discord.Embed()
            root = etree.fromstring(await resp.text(), etree.HTMLParser())

            nodes = root.findall(".//div[@class='mw-search-result-heading']/a")

            description = []
            special_pages = []
            fmt = '[`{0}`](http://en.cppreference.com{1})'
            special_fmt = '[{}](http://en.cppreference.com{})'
            for node in nodes:
                href = node.attrib['href']
                if not href.startswith('/w/cpp'):
                    continue

                if href.startswith(('/w/cpp/language', '/w/cpp/concept')):
                    # special page
                    special_pages.append(special_fmt.format(node.text, href))
                else:
                    description.append(fmt.format(node.text, href))

            if len(special_pages) > 0:
                e.add_field(name='Language Results', value='\n'.join(special_pages), inline=False)
                if len(description):
                    e.add_field(name='Library Results', value='\n'.join(description[:10]), inline=False)
            else:
                if not len(description):
                    return await self.bot.say('No results found.')

                e.title = 'Search Results'
                e.description = '\n'.join(description[:15])

            await self.bot.say(embed=e)

def setup(bot):
    bot.add_cog(Lounge(bot))

# coliru has no python3 Commands made for Lounge<C++>.      Don't abuse these. Compiles code via Coliru.          You have to pass in a code block with the language syntax         either set to one of these:          - cpp         - c         - python         - py         - haskell          Anything else isn't supported. The C++ compiler uses g++ -std=c++14.          The python support is only python2.7 (unfortunately).          Please don't spam this for Stacked's sake. # output is too big so post it in gist Search something on cppreference # special page