import asyncio, socket, pickle, threading
from kivy.clock import Clock
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from discord.ext import commands
import discord

class DiscordGUI(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.orientation = 'vertical'
        self.padding = [10, 10, 10, 10]
        intents = discord.Intents.default()
        intents.typing = False
        intents.presences = False
        self.bot = commands.Bot(command_prefix="!", intents=intents)
        self.channels = []
        self.selected_channel = None
        self.match_channel = None
        self.bot_token_entry = TextInput(hint_text="Bot Token:", multiline=False)
        self.server_id_entry = TextInput(hint_text="Server ID:", multiline=False)
        self.add_widget(self.bot_token_entry)
        self.add_widget(self.server_id_entry)
        self.add_widget(Button(text="Start Discord Bot", on_press=self.run_bot))
        self.fetch_button = Button(text="Fetch Channels", on_press=self.fetch_channels, disabled=True)
        self.add_widget(self.fetch_button)
        self.listen_button = Button(text="Listen", on_press=self.listen_server, disabled=True)
        self.add_widget(self.listen_button)
        self.channel_layout = GridLayout(cols=4, size_hint_y=None)
        self.channel_layout.bind(minimum_height=self.channel_layout.setter('height'))
        self.channel_buttons = ScrollView()
        self.channel_buttons.add_widget(self.channel_layout)
        self.add_widget(self.channel_buttons)

    def run_bot(self, instance):
        loop = asyncio.get_event_loop()
        self.bot_task = loop.create_task(self.bot.start(self.bot_token_entry.text.strip()))
        Clock.schedule_interval(lambda dt: loop.run_until_complete(asyncio.sleep(0)), 0.01)
        self.fetch_button.disabled = False

    def fetch_channels(self, instance):
        loop = asyncio.get_event_loop()
        loop.create_task(self._fetch_channels())

    async def _fetch_channels(self):
        guild_id = int(self.server_id_entry.text.strip())
        guild = self.bot.get_guild(guild_id)
        if guild:
            self.channels = [(channel.name, channel.id) for channel in guild.text_channels]
            Clock.schedule_once(lambda dt: self.update_buttons())

    def update_buttons(self):
        for widget in self.channel_layout.children[:]:
            if isinstance(widget, Button) and widget.text in [channel[0] for channel in self.channels]:
                self.channel_layout.remove_widget(widget)
        for channel in self.channels:
            button = Button(text=channel[0], size_hint_y=None, height=50)
            button.bind(on_press=self.toggle_button)
            self.channel_layout.add_widget(button)
        self.listen_button.disabled = False

    def toggle_button(self, instance):
        if self.selected_channel:
            self.selected_channel.background_color = (1, 1, 1, 1)
        instance.background_color = (0, 0, 0, 1)
        self.selected_channel = instance
        self.match_channel = next(channel[1] for channel in self.channels if channel[0] == instance.text)

    def listen_server(self, instance):
        if self.match_channel:
            listen_thread = threading.Thread(target=self._listen_server)
            listen_thread.start()

    def _listen_server(self):
        server_address = ('localhost', 12345)
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            sock.connect(server_address)
            while True:
                data = sock.recv(1024)
                if not data:
                    break
                shared_dict = pickle.loads(data)
                Clock.schedule_once(lambda dt: self.send_message_to_discord(shared_dict))

    def send_message_to_discord(self, match_data):
        if hasattr(self.selected_channel, 'text'):
            channel_id = self.match_channel
            channel = self.bot.get_channel(channel_id)
            if channel:
                asyncio.run_coroutine_threadsafe(channel.send(str(match_data)), self.bot.loop)

class DiscordApp(MDApp):
    def build(self):
        self.gui = DiscordGUI()
        return self.gui

    def on_stop(self):
        self.gui.bot_task.cancel()

if __name__ == "__main__":
    DiscordApp().run()