import asyncio, aiohttp, aiosocks, discord
import os, sys, linecache, async_timeout, inspect, traceback
import re, math, random, uuid, time, jsonpickle
from pymysql.converters import escape_item, escape_string, encoders
from contextlib import redirect_stdout
from discord.ext import commands
from discord.ext.commands.errors import CommandNotFound, CommandError
from discord.ext.commands.context import Context
from concurrent.futures import CancelledError, TimeoutError
from io import BytesIO, StringIO

encoders[ord(':')] = '\:'

class DataProtocol(asyncio.SubprocessProtocol):
	def __init__(self, exit_future):
		self.exit_future = exit_future
		self.output = bytearray()

	def pipe_data_received(self, fd, data):
		self.output.extend(data)

	def process_exited(self):
		try:
			self.exit_future.set_result(True)
		except:
			pass

	def pipe_connection_lost(self, fd, exc):
		try:
			self.exit_future.set_result(True)
		except:
			pass
	def connection_made(self, transport):
		self.transport = transport

	def connection_lost(self, exc):
		try:
			self.exit_future.set_result(True)
		except:
			pass

class Funcs():
	def __init__(self, bot, cursor):
		self.bot = bot
		self.cursor = cursor
		self.bot.google_api_keys = open(self.discord_path('utils/keys.txt')).read().split('\n')
		self.bot.google_count = 0
		self.image_mimes = ['image/png', 'image/pjpeg', 'image/jpeg', 'image/x-icon']
		self.session = aiohttp.ClientSession()
		self.mention_regex = re.compile(r"<@!?(?P<id>\d+)>")
		self.colors = ['red', 'blue', 'green', 'gold', 'dark_blue', 'dark_gold', 'dark_green', 'dark_grey', 'dark_magenta', 'dark_orange', 'dark_purple', 'dark_red', 'dark_teal', 'darker_grey', 'default', 'light_grey', 'lighter_grey', 'magenta', 'orange', 'purple', 'teal']
		self.color_count = 0

	def discord_path(self, path):
		return os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), path)

	def files_path(self, path):
		return self.discord_path('files/'+path)

	async def prefix_check(self, s, prefix, prefix_set):
		if prefix_set:
			return True
		count = 0
		for x in s:
			if count == 2:
				break
			elif count == 1:
				if x != prefix:
					break
			if x == prefix:
				count += 1
		if count == 1:
			return True
		return False

	async def get_prefix(self, message):
		if self.bot.dev_mode:
			prefix = ','
		elif self.bot.self_bot:
			prefix = 'self.'
		else:
			prefix = '.'
		prefix_set = False
		if message.channel.is_private is False and message.content.startswith(prefix+"prefix") is False:
			sql = "SELECT prefix FROM `prefix` WHERE server={0}"
			sql = sql.format(message.server.id)
			sql_channel = "SELECT prefix,channel FROM `prefix_channel` WHERE server={0} AND channel={1}"
			sql_channel = sql_channel.format(message.server.id, message.channel.id)
			result = self.cursor.execute(sql_channel).fetchall()
			if result:
				for s in result:
					if s['channel'] == message.channel.id:
						prefix = s['prefix']
						prefix_set = True
						break
			elif not prefix_set:
				result = self.cursor.execute(sql).fetchall()
				if len(result) != 0:
					prefix = result[0]['prefix']
					prefix_set = True
			if prefix_set:
				prefix = prefix.lower()
		mention = commands.bot.when_mentioned(self.bot, message)
		if message.content.startswith(mention):
			check = True
		else:
			check = await self.prefix_check(message.content, prefix, prefix_set)
		return [prefix, mention], check

	async def is_blacklisted(self, message):
		try:
			perms = message.channel.permissions_for(message.server.me)
			if perms.send_messages is False or perms.read_messages is False:
				return True
		except:
			pass
		if message.author.id == self.bot.owner.id:
			return False
		global_blacklist_result = self.cursor.execute('SELECT * FROM `global_blacklist` WHERE user={0}'.format(message.author.id)).fetchall()
		if message.channel.is_private:
			if len(global_blacklist_result) != 0 and message.author.id != bot.owner.id:
				return True
			return False
		muted_check_result = self.cursor.execute('SELECT * FROM `muted` WHERE server={0} AND id={1}'.format(message.server.id, message.author.id)).fetchall()
		if len(muted_check_result) != 0 and message.server.owner != message.author:
			return True
		server_blacklist_result = self.cursor.execute('SELECT * FROM `blacklist` WHERE server={0} AND user={1}'.format(message.server.id, message.author.id)).fetchall()
		channel_blacklist_result = self.cursor.execute('SELECT * FROM `channel_blacklist` WHERE server={0} AND channel={1}'.format(message.server.id, message.channel.id)).fetchall()
		if len(global_blacklist_result) != 0:
			return True
		elif len(server_blacklist_result) != 0:
			return True
		elif len(channel_blacklist_result) != 0:
			if 'blacklist' in message.content:
				return False
			return True
		return False

	async def command_check(self, message, command, prefix='.'):
		if message.author.id == self.bot.owner.id:
			return False
		sql = 'SELECT * FROM `command_blacklist` WHERE type="global" AND command={0}'
		sql = sql.format(self.escape(command))
		result = self.cursor.execute(sql).fetchall()
		if len(result) != 0:
			return True
		if message.channel.is_private:
			return False
		sql = 'SELECT * FROM `command_blacklist` WHERE server={0}'
		sql = sql.format(message.server.id)
		result = self.cursor.execute(sql).fetchall()
		topic_match = None
		if message.channel.topic:
			command_escape = re.escape(command)
			topic_regex = re.compile(r"((\[|\{)(\+|\-)?"+command_escape+r"(\]|\}))", re.I|re.S)
			match = topic_regex.findall(message.channel.topic.lower())
			if match:
				if match[0][2] == '+' or not len(match[0][2]):
					topic_match = False
				elif match[0][2] == '-':
					topic_match = True
		is_admin = False
		try:
			perms = message.channel.permissions_for(message.author)
			if perms.administrator or perms.manage_server or perms.manage_roles:
				is_admin = True
		except:
			pass
		if topic_match:
			await self.bot.send_message(message.channel, ':no_entry: **That command is disabled in this channel.**{0}'.format('\nRemove `[-{1}]` from the channel description to enable the command.' if is_admin else ''))
			return True
		for s in result:
			if s['command'] != command:
				continue
			if s['type'] == 'server':
				if topic_match is False:
					return False
				await self.bot.send_message(message.channel, ':no_entry: **That command is disabled on this server.**{0}'.format("\n`{0}command enable {1}` to enable the command.\n**Alternatively** place `[{1}]` in the channel topic or name.".format(prefix, command) if is_admin else ''))
				return True
			elif s['type'] == 'channel':
				if str(s['channel']) == str(message.channel.id):
					await self.bot.send_message(message.channel, ':no_entry: **That command is disabled in this channel.**{0}'.format("\n`{0}command enable channel {1}` {2} to enable the command".format(prefix, command, message.channel.mention) if is_admin else ''))
					return True
			elif s['type'] == 'role':
				for role in message.author.roles:
					if str(role.id) == str(s['role']):
						await self.bot.send_message(message.channel, ':no_entry: **That command is disabled for role: {1}**{0}'.format("\n`{0}command enable channel {1}` {2} to enable the command.".format(prefix, command, role.mention) if is_admin else ''), role.mention)
						return True
			elif s['type'] == 'user':
				if str(s['user']) == str(message.author.id):
					return True
		return False